//! PWM functionality tests for the Rockchip RK3588 on OrangePi 5 Plus.

use axplat_aarch64_opi5p::{
    GpioController, PwmAlignment, PwmPolarity, PwmRunMode, gpio_controller, make_pwm_config,
    pwm::{PWM_CHANNEL_COUNT, PWM_PMU_CLOCK_HZ},
    pwm_controller,
};
use gpio_rk3588::Level;
use pwm_rk3588::{Config as PwmConfig, RockchipPwm};
use timer_rk3588::{RK3588Timer, TimerMode};

/// Preferred PWM channel for board-level experiments (wired to the
/// fan/backlight header).
const TEST_PWM_CHANNEL: usize = 2;

/// GPIO3 bank index
const GPIO3_BANK: usize = 3;

/// Blue LED pin (GPIO3 pin 6)
const BLUE_LED_PIN: u8 = 6;

/// Green LED pin (GPIO3 pin 9, GPIO3_B1, supports PWM2_M1)
const GREEN_LED_PIN: u8 = 9;

/// PWM2 instance base address (PMU PWM block)
/// Matches the address used by the shared PWM controller implementation.
const PWM2_BASE_PADDR: usize = axplat_aarch64_opi5p::pwm::PWM2_BASE_PADDR;

/// CRU (Clock & Reset Unit) base address
const CRU_BASE_PADDR: usize = 0xFD7C0000;

/// CRU clock gate register for PWM2 (CLKGATE_CON15 offset)
const CRU_CLKGATE_CON15_OFFSET: usize = 0x083C;

/// CRU software reset register for PWM2 (SOFTRST_CON15 offset)
const CRU_SOFTRST_CON15_OFFSET: usize = 0x0A3C;

/// PWM channel for green LED - using PWM2 channel 0
const GREEN_LED_PWM_CHANNEL: usize = 0;

/// BUS_IOC base address for IOMUX configuration
const BUS_IOC_BASE: usize = 0xFD5F8000;

/// IOMUX register offset for GPIO3B (low 4 pins: GPIO3_B0-B3)
const GPIO3B_IOMUX_SEL_L_OFFSET: usize = 0x007C;

/// IOMUX value for GPIO3_B1 configured as PWM2_M1 (bits [7:4] = 0x3)
const GPIO3B1_PWM2_M1_FUNC: u32 = 0x3;

/// IOMUX bit position for GPIO3_B1 (pin 1, so bits [7:4])
const GPIO3B1_IOMUX_SHIFT: u32 = 4;

/// TIMER_PMU base address for software PWM
const TIMER_PMU_BASE: usize = 0xFD8F0000;

/// Timer clock frequency (24MHz)
const TIMER_FREQ: u64 = 24_000_000;

/// Software PWM frequency (1 kHz = 1ms period)
const SOFT_PWM_FREQ_HZ: u64 = 1000;

/// Software PWM period in timer ticks (24MHz / 1kHz = 24000 ticks per period)
const SOFT_PWM_PERIOD_TICKS: u64 = TIMER_FREQ / SOFT_PWM_FREQ_HZ;

/// Simple busy-wait delay for observable effects.
fn delay(count: usize) {
    for _ in 0..count {
        core::hint::spin_loop();
    }
}

fn log_state(prefix: &str, pwm: &RockchipPwm) {
    let state = pwm.state();
    info!(
        "{prefix}: counter={} period={} duty={} enabled={} alignment={:?} mode={:?}",
        state.counter, state.period, state.duty, state.enabled, state.alignment, state.mode
    );

    let period_ns = state.period_ns(PWM_PMU_CLOCK_HZ, pwm.prescaler());
    let duty_ns = state.duty_ns(PWM_PMU_CLOCK_HZ, pwm.prescaler());
    info!("{prefix}: period_ns={} duty_ns={}", period_ns, duty_ns);
}

fn configure_pwm(channel: &RockchipPwm, config: &PwmConfig) {
    match channel.configure(config) {
        Ok(_) => {
            log_state("Configured", channel);

            let state = channel.state();

            let expected_period = (((PWM_PMU_CLOCK_HZ as u128) * (config.period_ns as u128)
                + 500_000_000u128)
                / 1_000_000_000u128) as u32;
            let expected_duty = (((PWM_PMU_CLOCK_HZ as u128) * (config.duty_ns as u128)
                + 500_000_000u128)
                / 1_000_000_000u128) as u32;

            if state.period != expected_period || state.duty != expected_duty {
                warn!(
                    "PWM state mismatch: expected period={} duty={}, got period={} duty={}",
                    expected_period, expected_duty, state.period, state.duty
                );
            }
        }
        Err(err) => {
            error!("Failed to configure PWM: {:?}", err);
        }
    }
}

/// Runs a basic configuration test at 1kHz with 50% duty cycle.
pub fn run_pwm_basic_test() {
    info!("=== Starting PWM basic configuration test ===");

    if TEST_PWM_CHANNEL >= PWM_CHANNEL_COUNT {
        error!(
            "Requested PWM channel {} is out of range (max {})",
            TEST_PWM_CHANNEL,
            PWM_CHANNEL_COUNT - 1
        );
        return;
    }

    let controller = pwm_controller();
    let pwm_lock = match controller.channel(TEST_PWM_CHANNEL) {
        Some(lock) => lock,
        None => {
            error!("PWM channel {} unavailable", TEST_PWM_CHANNEL);
            return;
        }
    };

    let pwm = pwm_lock.lock();

    let config = make_pwm_config(
        1_000_000, // 1 kHz period (1 ms)
        500_000,   // 50% duty cycle
        PwmPolarity::Normal,
        PwmAlignment::Left,
        PwmRunMode::Continuous,
        true,
    );

    configure_pwm(&pwm, &config);
    delay(5_000_000);

    pwm.disable();
    info!("=== PWM basic configuration test completed ===");
}

/// Sweeps the duty cycle from 10% to 90% in steps to showcase dynamic
/// reconfiguration.
pub fn run_pwm_duty_sweep() {
    info!("=== Starting PWM duty sweep test ===");

    let controller = pwm_controller();
    let pwm_lock = match controller.channel(TEST_PWM_CHANNEL) {
        Some(lock) => lock,
        None => {
            error!("PWM channel {} unavailable", TEST_PWM_CHANNEL);
            return;
        }
    };

    let pwm = pwm_lock.lock();

    let period_ns = 500_000; // 2 kHz
    for step in 1..=9 {
        let duty_ns = (period_ns / 10) * step as u64;
        let config = make_pwm_config(
            period_ns,
            duty_ns,
            PwmPolarity::Normal,
            PwmAlignment::Left,
            PwmRunMode::Continuous,
            true,
        );
        info!(
            "Setting duty sweep step {}/9 ({} ns / {} ns)",
            step, duty_ns, period_ns
        );
        configure_pwm(&pwm, &config);
        delay(3_000_000);
    }

    pwm.disable();
    info!("=== PWM duty sweep test completed ===");
}

/// Helper function: Microsecond delay using timer
fn timer_delay_us(timer: &RK3588Timer, microseconds: u64) {
    let target_ticks = (TIMER_FREQ * microseconds) / 1_000_000;

    timer.disable();
    timer.load_count(target_ticks);
    timer.set_mode(TimerMode::UserDefined);
    timer.disable_interrupt();
    timer.enable();

    let initial_value = timer.current_value();

    // Poll until timer completes
    loop {
        let current = timer.current_value();

        // Check if timer has stopped (value stays constant) - give it a moment to start
        if current == initial_value && timer.current_value() == current {
            for _ in 0..1000 {
                core::hint::spin_loop();
            }
            continue;
        }

        // For increment timer: check if reached target
        if current >= target_ticks {
            break;
        }

        // For decrement timer: check if reached 0
        if current == 0 && initial_value != 0 {
            break;
        }

        core::hint::spin_loop();
    }

    timer.disable();
}

/// Configure IOMUX to switch GPIO3_B1 to PWM2_M1 mode
///
/// This function configures the IO multiplexer (IOMUX) to route PWM2 signal
/// to GPIO3_B1 (green LED pin). The RK3588 uses a write-mask mechanism:
/// - High 16 bits: write mask (set bit to enable writing corresponding low bit)
/// - Low 16 bits: actual configuration value
///
/// For GPIO3_B1 (bits [7:4]):
/// - Write mask: 0xF << (4 + 16) = 0xF0000
/// - Value: 0x3 << 4 = 0x30 (PWM2_M1 function)
fn configure_gpio3b1_to_pwm2() {
    use core::ptr::{read_volatile, write_volatile};

    // Convert physical address to virtual address
    let iomux_reg_vaddr =
        axplat::mem::phys_to_virt(axplat::mem::pa!(BUS_IOC_BASE + GPIO3B_IOMUX_SEL_L_OFFSET))
            .as_usize();

    info!("IOMUX register physical addr: 0x{:08X}", BUS_IOC_BASE + GPIO3B_IOMUX_SEL_L_OFFSET);
    info!("IOMUX register virtual addr: 0x{:016X}", iomux_reg_vaddr);

    // Read current value
    let current_val = unsafe { read_volatile(iomux_reg_vaddr as *const u32) };
    info!("IOMUX current value: 0x{:08X}", current_val);

    // RK3588 IOMUX uses write-mask mechanism:
    // [31:16] = write mask (1 = allow write to corresponding bit in [15:0])
    // [15:0]  = actual value
    //
    // For GPIO3_B1 (bits [7:4]), we need:
    // - Write mask: 0xF << (4 + 16) to enable writing bits [7:4]
    // - Value: GPIO3B1_PWM2_M1_FUNC << 4 to set PWM2_M1 function
    let write_mask = 0xF << (GPIO3B1_IOMUX_SHIFT + 16);
    let value = GPIO3B1_PWM2_M1_FUNC << GPIO3B1_IOMUX_SHIFT;
    let iomux_val = write_mask | value;

    info!("IOMUX write value: 0x{:08X} (mask: 0x{:08X}, val: 0x{:08X})",
          iomux_val, write_mask, value);

    unsafe {
        write_volatile(iomux_reg_vaddr as *mut u32, iomux_val);
    }

    // Read back to verify
    let new_val = unsafe { read_volatile(iomux_reg_vaddr as *const u32) };
    info!("IOMUX new value: 0x{:08X}", new_val);
    info!("IOMUX GPIO3_B1 bits [7:4]: 0x{:X}", (new_val >> 4) & 0xF);

    info!("IOMUX configured: GPIO3_B1 -> PWM2_M1");
}

/// Enable PWM2 clock via CRU (Clock & Reset Unit)
fn enable_pwm2_clock() {
    use core::ptr::{read_volatile, write_volatile};

    // CRU CLKGATE_CON15: controls PWM2 bus and functional clocks
    // Bit 6: PCLK_PWM2 (0 = enabled, 1 = gated)
    // Bit 7: CLK_PWM2  (0 = enabled, 1 = gated)
    let cru_clkgate_vaddr =
        axplat::mem::phys_to_virt(axplat::mem::pa!(CRU_BASE_PADDR + CRU_CLKGATE_CON15_OFFSET))
            .as_usize();

    info!("CRU CLKGATE_CON15 physical addr: 0x{:08X}", CRU_BASE_PADDR + CRU_CLKGATE_CON15_OFFSET);
    info!("CRU CLKGATE_CON15 virtual addr: 0x{:016X}", cru_clkgate_vaddr);

    // Read current value
    let current_val = unsafe { read_volatile(cru_clkgate_vaddr as *const u32) };
    info!("CRU CLKGATE_CON15 current value: 0x{:08X}", current_val);

    // Enable PWM2 clocks: clear bits 6 and 7 (using write mask in upper half word)
    let write_mask = (1 << (6 + 16)) | (1 << (7 + 16));
    let value = 0; // clear target bits
    let clkgate_val = write_mask | value;

    info!("CRU write value: 0x{:08X} (enabling PWM2 clock)", clkgate_val);

    unsafe {
        write_volatile(cru_clkgate_vaddr as *mut u32, clkgate_val);
    }

    // Read back to verify
    let new_val = unsafe { read_volatile(cru_clkgate_vaddr as *const u32) };
    info!("CRU CLKGATE_CON15 new value: 0x{:08X}", new_val);
    info!(
        "PWM2 clock bits [7:6]: {}{} (bit7=CLK_PWM2, bit6=PCLK_PWM2; 0=enabled, 1=gated)",
        (new_val >> 7) & 1,
        (new_val >> 6) & 1
    );

    info!("PWM2 clock enabled");
}

/// De-assert PWM2 reset via CRU (Clock & Reset Unit)
fn deassert_pwm2_reset() {
    use core::ptr::{read_volatile, write_volatile};

    // CRU SOFTRST_CON15: controls PWM2 reset (bus + core)
    // Bit 6: SRST_P_PWM2 (0 = running, 1 = in reset)
    // Bit 7: SRST_PWM2   (0 = running, 1 = in reset)
    let cru_softrst_vaddr =
        axplat::mem::phys_to_virt(axplat::mem::pa!(CRU_BASE_PADDR + CRU_SOFTRST_CON15_OFFSET))
            .as_usize();

    info!("CRU SOFTRST_CON15 physical addr: 0x{:08X}", CRU_BASE_PADDR + CRU_SOFTRST_CON15_OFFSET);
    info!("CRU SOFTRST_CON15 virtual addr: 0x{:016X}", cru_softrst_vaddr);

    // Read current value
    let current_val = unsafe { read_volatile(cru_softrst_vaddr as *const u32) };
    info!("CRU SOFTRST_CON15 current value: 0x{:08X}", current_val);

    // De-assert PWM2 reset (clear bits 6 and 7)
    let write_mask = (1 << (6 + 16)) | (1 << (7 + 16));
    let value = 0;
    let softrst_val = write_mask | value;

    info!("CRU write value: 0x{:08X} (de-asserting PWM2 reset)", softrst_val);

    unsafe {
        write_volatile(cru_softrst_vaddr as *mut u32, softrst_val);
    }

    // Read back to verify
    let new_val = unsafe { read_volatile(cru_softrst_vaddr as *const u32) };
    info!("CRU SOFTRST_CON15 new value: 0x{:08X}", new_val);
    info!(
        "PWM2 reset bits [7:6]: {}{} (bit7=SRST_PWM2, bit6=SRST_P_PWM2; 0=running, 1=reset)",
        (new_val >> 7) & 1,
        (new_val >> 6) & 1
    );

    info!("PWM2 reset de-asserted");
}

/// Generate software PWM signal for one period with specified duty cycle
/// duty_percent: 0-100, representing the percentage of time LED is HIGH
fn soft_pwm_period(gpio: &GpioController, timer: &RK3588Timer, duty_percent: u64) {
    if duty_percent == 0 {
        // 0% duty cycle: always off
        gpio.set_output(GPIO3_BANK, GREEN_LED_PIN, Level::Low);
        timer_delay_us(timer, 1000); // Wait 1ms (period)
        return;
    }

    if duty_percent >= 100 {
        // 100% duty cycle: always on
        gpio.set_output(GPIO3_BANK, GREEN_LED_PIN, Level::High);
        timer_delay_us(timer, 1000); // Wait 1ms (period)
        return;
    }

    // Calculate high and low time in microseconds (1ms = 1000us period)
    let period_us = 1000u64; // 1 kHz = 1ms period
    let high_time_us = (period_us * duty_percent) / 100;
    let low_time_us = period_us - high_time_us;

    // High phase
    if high_time_us > 0 {
        gpio.set_output(GPIO3_BANK, GREEN_LED_PIN, Level::High);
        timer_delay_us(timer, high_time_us);
    }

    // Low phase
    if low_time_us > 0 {
        gpio.set_output(GPIO3_BANK, GREEN_LED_PIN, Level::Low);
        timer_delay_us(timer, low_time_us);
    }
}

/// LED breathing effect: gradually brightens and dims the green LED using
/// software PWM.
///
/// This function controls the green LED (GPIO3_B1, pin 9) using GPIO + Timer:
/// - GPIO driver: controls the digital output level (HIGH/LOW)
/// - Timer driver: provides precise timing for duty cycle control
///
/// Software PWM principle:
/// 1. Set a fixed period (e.g., 1ms for 1 kHz frequency)
/// 2. Within each period, set GPIO HIGH for (duty_cycle)% of the time
/// 3. Set GPIO LOW for the remaining (100 - duty_cycle)% of the time
/// 4. Higher duty cycle = LED on longer = brighter
///
/// Steps:
/// 1. Initialize GPIO (set green LED pin as output)
/// 2. Initialize Timer for precise microsecond delays
/// 3. Generate PWM by toggling GPIO with timer-controlled delays
/// 4. Gradually adjust duty cycle for breathing effect
pub fn run_pwm_led_breathing() {
    info!("=== Starting Software PWM LED breathing test (Green LED via GPIO + Timer) ===");

    // Initialize GPIO controller
    let gpio = gpio_controller();
    info!("GPIO controller initialized");

    // Initialize timer for precise PWM timing
    let mut timer = RK3588Timer::new(TIMER_PMU_BASE, 1); // Use channel 1
    if !timer.init() {
        error!("Failed to initialize timer");
        return;
    }
    info!(
        "Timer initialized (channel 1, {} MHz clock)",
        TIMER_FREQ / 1_000_000
    );

    // Software PWM configuration
    let pwm_freq_hz = SOFT_PWM_FREQ_HZ;
    let min_duty_percent = 5u64; // Minimum brightness
    let max_duty_percent = 100u64; // Maximum brightness
    let brightness_steps = 50; // Steps for brightness transition
    let periods_per_step = 20; // Number of PWM periods per brightness step

    info!("Software PWM Configuration:");
    info!(
        "  - Frequency: {} Hz (period: {} us)",
        pwm_freq_hz,
        1_000_000 / pwm_freq_hz
    );
    info!(
        "  - Duty cycle range: {}% to {}%",
        min_duty_percent, max_duty_percent
    );
    info!("  - Brightness steps: {}", brightness_steps);
    info!("  - PWM periods per step: {}", periods_per_step);
    info!("  - Control method: GPIO toggle + Timer delay");

    // Breathing cycle: 3 complete breath cycles
    for cycle in 1..=3 {
        info!("Breathing cycle {}/3", cycle);

        // Phase 1: Gradually brighten (increase duty cycle)
        for step in 0..=brightness_steps {
            let duty_percent = min_duty_percent
                + ((max_duty_percent - min_duty_percent) * step) / brightness_steps;

            // Generate multiple PWM periods at this duty cycle for visible effect
            for _ in 0..periods_per_step {
                soft_pwm_period(&gpio, &timer, duty_percent);
            }
        }

        // Phase 2: Gradually dim (decrease duty cycle)
        for step in (0..=brightness_steps).rev() {
            let duty_percent = min_duty_percent
                + ((max_duty_percent - min_duty_percent) * step) / brightness_steps;

            // Generate multiple PWM periods at this duty cycle
            for _ in 0..periods_per_step {
                soft_pwm_period(&gpio, &timer, duty_percent);
            }
        }
    }

    // Clean up: ensure LED is off
    gpio.set_output(GPIO3_BANK, GREEN_LED_PIN, Level::Low);
    timer.disable();
    info!("Software PWM stopped, green LED off");
    info!("=== Software PWM LED breathing test completed ===");
}

/// TODO: fix it
/// LED breathing effect using hardware PWM: gradually brightens and dims the green LED.
///
/// This function controls the green LED (GPIO3_B1, pin 9) using hardware PWM module:
/// - PWM Channel 2 (PWM2_M1): Hardware PWM controller for precise signal generation
/// - IOMUX: Automatically configures GPIO3_B1 to PWM2_M1 mode
///
/// Hardware PWM advantages:
/// 1. No CPU overhead - PWM signal generated by dedicated hardware
/// 2. Precise frequency and duty cycle control
/// 3. Smooth operation without software interrupts
/// 4. Can run in background while CPU does other work
///
/// Steps:
/// 1. Acquire PWM Channel 2 (mapped to green LED pin)
/// 2. Configure PWM frequency (1 kHz) and initial duty cycle
/// 3. Gradually adjust duty cycle for breathing effect
/// 4. Disable PWM when complete
pub fn run_pwm_led_breathing_hardware() {
    info!("=== Starting Hardware PWM LED breathing test (Green LED via PWM2_M1) ===");

    // Step 1: Enable PWM2 clock via CRU
    info!("Step 1: Enabling PWM2 clock");
    enable_pwm2_clock();

    // Step 2: De-assert PWM2 reset via CRU
    info!("Step 2: De-asserting PWM2 reset");
    deassert_pwm2_reset();

    // Step 3: Configure IOMUX to switch GPIO3_B1 from GPIO mode to PWM2_M1 mode
    info!("Step 3: Configuring IOMUX: GPIO3_B1 -> PWM2_M1");
    configure_gpio3b1_to_pwm2();

    // Step 4: Initialize PWM2 controller (NOT PMU_PWM!)
    info!("Step 4: Initializing PWM2 controller at 0x{:08X}", PWM2_BASE_PADDR);
    use pwm_rk3588::RockchipPwm;
    let pwm2_vaddr = axplat::mem::phys_to_virt(axplat::mem::pa!(PWM2_BASE_PADDR)).as_usize();
    let pwm2 = RockchipPwm::new(pwm2_vaddr, 24_000_000); // 24MHz clock

    info!("PWM2 controller initialized at vaddr 0x{:016X}", pwm2_vaddr);

    // Log current PWM state
    let initial_state = pwm2.state();
    info!("Initial PWM state: period={} duty={} enabled={}",
          initial_state.period, initial_state.duty, initial_state.enabled);

    // Hardware PWM Configuration:
    // - Period: 1ms (1 kHz) - suitable for LED brightness control without flicker
    // - Duty cycle: varies from 5% to 100% for smooth brightness transition
    // - Mode: Continuous - hardware keeps generating PWM signal
    // - Alignment: Left - standard edge-aligned PWM
    // - Polarity: Normal - high level during duty cycle (LED on)
    let period_ns = 1_000_000; // 1 kHz PWM frequency (1ms period)
    let brightness_steps = 100; // 100 steps for smooth brightness transition
    let delay_per_step = 5_000_000; // ~50ms per step for visible smooth effect

    let min_duty_percent = 5; // Minimum brightness (5% duty cycle)
    let max_duty_percent = 100; // Maximum brightness (100% duty cycle)

    info!("Hardware PWM Configuration:");
    info!(
        "  - Frequency: {} Hz (period: {} ns)",
        1_000_000_000 / period_ns,
        period_ns
    );
    info!(
        "  - Duty cycle range: {}% to {}%",
        min_duty_percent, max_duty_percent
    );
    info!("  - Brightness steps: {}", brightness_steps);
    info!("  - Mode: Continuous, Left-aligned, Normal polarity");
    info!("  - Control method: Hardware PWM module");

    // Test 1: Set to 100% duty cycle first to verify PWM works
    info!("Test: Setting PWM to 100% duty cycle for 2 seconds...");
    let test_config = make_pwm_config(
        period_ns,
        period_ns, // 100% duty
        PwmPolarity::Normal,
        PwmAlignment::Left,
        PwmRunMode::Continuous,
        true,
    );

    // Add direct register inspection
    use core::ptr::{read_volatile, write_volatile};
    info!("Direct register inspection (vaddr: 0x{:016X})", pwm2_vaddr);
    let ctrl_before = unsafe { read_volatile((pwm2_vaddr + 0x0C) as *const u32) };
    let period_before = unsafe { read_volatile((pwm2_vaddr + 0x04) as *const u32) };
    let duty_before = unsafe { read_volatile((pwm2_vaddr + 0x08) as *const u32) };
    info!("Before config: CTRL=0x{:08X} PERIOD={} DUTY={}", ctrl_before, period_before, duty_before);

    // Test: Try writing DUTY register directly before using driver
    info!("Test: Writing DUTY register directly...");
    unsafe {
        // Enable LOCK first
        write_volatile((pwm2_vaddr + 0x0C) as *mut u32, 0x40); // Set LOCK_EN (bit 6)
        write_volatile((pwm2_vaddr + 0x08) as *mut u32, 12000); // Write DUTY = 50%
        write_volatile((pwm2_vaddr + 0x0C) as *mut u32, 0x00); // Clear LOCK_EN
    }
    let duty_direct = unsafe { read_volatile((pwm2_vaddr + 0x08) as *const u32) };
    info!("DUTY after direct write: {}", duty_direct);

    if let Err(e) = pwm2.configure(&test_config) {
        error!("Failed to configure PWM test: {:?}", e);
        return;
    }

    // Check registers after configuration
    let ctrl_after = unsafe { read_volatile((pwm2_vaddr + 0x0C) as *const u32) };
    let period_after = unsafe { read_volatile((pwm2_vaddr + 0x04) as *const u32) };
    let duty_after = unsafe { read_volatile((pwm2_vaddr + 0x08) as *const u32) };
    info!("After config:  CTRL=0x{:08X} PERIOD={} DUTY={}", ctrl_after, period_after, duty_after);

    let test_state = pwm2.state();
    info!("Test PWM state: period={} duty={} enabled={}",
          test_state.period, test_state.duty, test_state.enabled);

    delay(100_000_000); // 2 seconds

    info!("Starting breathing cycle...");

    // Breathing cycle: 3 complete breath cycles
    for cycle in 1..=3 {
        info!("Breathing cycle {}/3", cycle);

        // Phase 1: Gradually brighten (increase duty cycle from min to max)
        for step in 0..=brightness_steps {
            let duty_percent =
                min_duty_percent + ((max_duty_percent - min_duty_percent) * step) / brightness_steps;
            let duty_ns = (period_ns * duty_percent as u64) / 100;

            // Debug logging for first few steps
            if cycle == 1 && step < 3 {
                info!("Step {}: duty_percent={}, duty_ns={}", step, duty_percent, duty_ns);
            }

            let config = make_pwm_config(
                period_ns,
                duty_ns,
                PwmPolarity::Normal,    // High level during duty cycle
                PwmAlignment::Left,     // Left-aligned PWM
                PwmRunMode::Continuous, // Continuous output
                true,                   // Enable PWM immediately
            );

            if let Err(e) = pwm2.configure(&config) {
                error!("Failed to configure PWM (brighten phase): {:?}", e);
                pwm2.disable();
                return;
            }

            // Log first configuration for debugging
            if cycle == 1 && step == 0 {
                let state = pwm2.state();
                info!("First PWM config: period={} duty={} enabled={}",
                      state.period, state.duty, state.enabled);
            }

            delay(delay_per_step);
        }

        // Phase 2: Gradually dim (decrease duty cycle from max to min)
        for step in (0..=brightness_steps).rev() {
            let duty_percent =
                min_duty_percent + ((max_duty_percent - min_duty_percent) * step) / brightness_steps;
            let duty_ns = (period_ns * duty_percent as u64) / 100;

            let config = make_pwm_config(
                period_ns,
                duty_ns,
                PwmPolarity::Normal,
                PwmAlignment::Left,
                PwmRunMode::Continuous,
                true,
            );

            if let Err(e) = pwm2.configure(&config) {
                error!("Failed to configure PWM (dim phase): {:?}", e);
                pwm2.disable();
                return;
            }

            delay(delay_per_step);
        }
    }

    // Clean up: disable hardware PWM output
    pwm2.disable();
    info!("Hardware PWM disabled, green LED should be off");
    info!("=== Hardware PWM LED breathing test completed ===");
}

/// Executes all PWM test scenarios.
pub fn run_all_pwm_tests() {
    // run_pwm_basic_test();
    // run_pwm_duty_sweep();
    // run_pwm_led_breathing();
    run_pwm_led_breathing_hardware();
}
